/**
 * 
 */
package com.gragra.util;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntList;
/**
 * @author Christoph Teichmann
 * created Jan 13, 2013 2:52:13 PM
 * @version 0.1
 */
public class DeepIntNumberer
{
	/**
	 * 
	 */
	private final DeepIntNode head = new DeepIntNode();
	/**
	 * 
	 *
	 * @param code
	 * @return
	 */
	public int lookUp(int[] code)
	{
		DeepIntNode d = head;
		int l = code.length;
		for(int i=0;i<l;++i)
		{
			d = d.getFollower(code[i]);
			if(d == null)
			{return -1;}
		}
		return d.code;
	}
	/** 
	 *
	 * @param list
	 * @return
	 */
	public int ensure(IntList list)
	{
		DeepIntNode din = head;
		IntIterator it = list.iterator();
		while(it.hasNext())
		{din = din.getFollowerSafe(it.nextInt());}
		int i = din.code;
		if(i < 0)
		{return i = din.code = lastUsed++;}
		return i;
	}
	
	public int getNumberIfExisting(IntList list)
	{
		DeepIntNode d = head;
		int l = list.size();
		for(int i=0;i<l;++i)
		{
			d = d.getFollower(list.getInt(i));
			if(d == null)
			{return -1;}
		}
		return d.code;
	}
	/**
	 * 
	 */
	private int lastUsed = 0;
	/**
	 * 
	 *
	 * @param code
	 * @return
	 */
	public int assignNew(int[] code)
	{
		DeepIntNode d = head;
		int l = code.length;
		for(int i=0;i<l;++i)
		{d = d.getFollowerSafe(code[i]);}
		return d.code = lastUsed++;
	}
	/**
	 * 
	 * @author Christoph Teichmann
	 * created Jan 13, 2013 2:57:10 PM
	 * @version 0.1
	 */
	private class DeepIntNode
	{
		/**
		 * 
		 */
		private int code = -1;
		/**
		 * 
		 */
		private Int2ObjectMap<DeepIntNode> followers = new Int2ObjectOpenHashMap<DeepIntNumberer.DeepIntNode>();
		/**
		 * 
		 *
		 * @param step
		 * @return
		 */
		private DeepIntNode getFollowerSafe(int step)
		{
			DeepIntNode din = followers.get(step);
			if(din == null)
			{
				din = new DeepIntNode();
				followers.put(step, din);
			}
			return din;
		}
		/**
		 * 
		 *
		 * @param step
		 * @return
		 */
		private DeepIntNode getFollower(int step)
		{return followers.get(step);}
	}
	/**
	 *
	 * @param f
	 * @return
	 */
	public int ensure(int[] f)
	{
		DeepIntNode din = head;
		for(int i=0;i<f.length;++i)
		{din = din.getFollowerSafe(f[i]);}
		int i = din.code;
		if(i < 0)
		{return i = din.code = lastUsed++;}
		return i;
	}
}